#include "Game.h"
#include <iostream>
#include <limits>
#include <random>
//`Game.cpp` implements a text - based adventure game. It initializes game elements, runs a loop for player actions, and checks win/loss conditions to end the game.
Game::Game() : player(nullptr), currentRoom(nullptr), gameOver(false), playerWon(false) {
    // Create player
    std::string playerName;
    std::cout << "==== Welcome to the Text Adventure Game ====" << std::endl;
    std::cout <<"Winning condition: Success is achieved if the boss is defeated."<<std::endl;
    std::cout << "Please enter your name: ";
    std::getline(std::cin, playerName);
    
    player = new Player(playerName);
    
    // Initialize game world
    initRooms();
    connectRooms();
    initItems();
    initEnemies();
    
    // Set starting room
    if (!rooms.empty()) {
        currentRoom = rooms[0];
    }
}

Game::~Game() {
    // Release player memory
    delete player;
    
    // Release all room memory
    for (Room* room : rooms) {
        delete room;
    }
    rooms.clear();
}

void Game::initRooms() {
    // Create all rooms in the game
    Room* startRoom = new Room("Starting Hall", "You stand in a spacious hall with ancient tapestries hanging on the walls. There are chairs and a table in the corner.", 
                              RoomType::HEALING, 3); // Healing room
    
    Room* corridor = new Room("Corridor", "This is a narrow corridor with portraits hanging on both sides of the walls.");
    
    Room* library = new Room("Library", "The shelves are filled with ancient books and scrolls. The air smells of old paper.", 
                            RoomType::DAMAGING, 2); // Damaging room
    
    Room* treasureRoom = new Room("Treasure Room", "This room is filled with gold coins and jewels, with a large chest in the corner.", 
                                 RoomType::TREASURE, 0); // Treasure room
    
    // Add rooms to the game room list
    rooms.push_back(startRoom);
    rooms.push_back(corridor);
    rooms.push_back(library);
    rooms.push_back(treasureRoom);
}

void Game::connectRooms() {
    // Set connections between rooms
    if (rooms.size() >= 4) {
        // Starting Hall --> Corridor
        rooms[0]->setExit("East", rooms[1]);
        // Corridor --> Starting Hall
        rooms[1]->setExit("West", rooms[0]);
        
        // Corridor --> Library
        rooms[1]->setExit("North", rooms[2]);
        // Library --> Corridor
        rooms[2]->setExit("South", rooms[1]);
        
        // Library --> Treasure Room
        rooms[2]->setExit("East", rooms[3]);
        // Treasure Room --> Library
        rooms[3]->setExit("West", rooms[2]);
    }
}

void Game::initItems() {
    // Add items to the Starting Hall
    if (rooms.size() >= 1) {
        rooms[0]->addItem(new Item("Magic Amulet", "A mysterious amulet that seems to increase the wearer's vitality.", 10, 2, 0));
    }
    
    // Add items to the Corridor
    if (rooms.size() >= 2) {
        rooms[1]->addItem(new Item("Wooden Shield", "A simple wooden shield that provides basic protection.", 5, 1, 3));
    }
    
    // Add items to the Library
    if (rooms.size() >= 3) {
        rooms[2]->addItem(new Item("Ancient Scroll", "A scroll containing ancient magic that increases attack power when read.", 0, 5, 2));
    }
    
    // Add items to the Treasure Room
    if (rooms.size() >= 4) {
        rooms[3]->addItem(new Item("Legendary Sword", "A sword gleaming with light. Holding it makes you feel powerful.", 15, 7, 5));
    }
}

void Game::initEnemies() {
    // Add goblin enemy to the Corridor
    if (rooms.size() >= 2) {
        rooms[1]->addEnemy(new Goblin("Goblin Warrior", "A small but agile green creature with a rusty short sword.", 
                                    15, 8, 8, 50, 0.3f)); // 30% critical chance
    }
    
    // Add ghost enemy to the Library
    if (rooms.size() >= 3) {
        rooms[2]->addEnemy(new Ghost("Bookshelf Specter", "A semi-transparent ghost that floats between bookshelves.", 
                                   20, 10, 11, 75, 0.4f)); // 40% dodge rate
    }
    
    // Add BOSS to the Treasure Room
    if (rooms.size() >= 4) {
        rooms[3]->addEnemy(new Boss("Ancient Guardian", "A giant stone statue, seemingly the guardian of the treasure, with eyes glowing red.", 
                                  30, 15, 12, 200, 5)); // Healing power of 5
    }
}

void Game::run() {
    std::cout << "\nYou wake up to find yourself in a mysterious place. Legend says there's treasure here, but also dangerous guardians...\n" << std::endl;
    
    // Game main loop
    while (!gameOver) {
        if (currentRoom) {
            // Display current room information
            currentRoom->displayRoom();
            
            // Display player status
            std::cout << "Your HP: " << player->getHealth() << "/" << player->getMaxHealth() << std::endl << std::endl;
            
            // Show menu and get player choice
            int choice = showMenu();
            
            // Process player choice
            processCommand(choice);
            
            // Check if game is over
            checkGameOver();
        } else {
            std::cout << "Error: Current room does not exist. Game over." << std::endl;
            gameOver = true;
        }
    }
    
    // Game over message
    if (playerWon) {
        std::cout << "\n========= VICTORY =========" << std::endl;
        std::cout << "Congratulations! You've won! You found the legendary treasure and defeated all enemies!" << std::endl;
        std::cout << "Your final level: " << player->getLevel() << std::endl;
        std::cout << "Your final stats: " << std::endl;
        std::cout << "Health: " << player->getHealth() << "/" << player->getMaxHealth() << std::endl;
        std::cout << "Attack: " << player->getAttack() << std::endl;
        std::cout << "Defense: " << player->getDefense() << std::endl;
        std::cout << "===========================" << std::endl;
    } else {
        std::cout << "\n========= GAME OVER =========" << std::endl;
        std::cout << "You were defeated..." << std::endl;
        std::cout << "Don't give up, try again next time!" << std::endl;
        std::cout << "===========================" << std::endl;
    }
}

int Game::showMenu() const {
    std::vector<std::string> options;
    
    // Add movement options
    for (const auto& exit : currentRoom->getExits()) {
        options.push_back("Move to " + exit.first);
    }
    
    // Add pickup item options
    const std::vector<Item*>& items = currentRoom->getItems();
    for (size_t i = 0; i < items.size(); ++i) {
        options.push_back("Pick up " + items[i]->name);
    }
    
    // Add attack enemy options
    const std::vector<Enemy*>& enemies = currentRoom->getEnemies();
    for (size_t i = 0; i < enemies.size(); ++i) {
        options.push_back("Attack " + enemies[i]->getName());
    }
    
    // Add view inventory and status options
    options.push_back("View inventory");
    options.push_back("View status");
    
    // Display options menu
    std::cout << "What do you want to do?" << std::endl;
    for (size_t i = 0; i < options.size(); ++i) {
        std::cout << (i + 1) << ". " << options[i] << std::endl;
    }
    
    // Get player input
    int choice = 0;
    while (choice < 1 || choice > static_cast<int>(options.size())) {
        std::cout << "Enter your choice (1-" << options.size() << "): ";
        std::cin >> choice;
        
        if (std::cin.fail()) {
            std::cin.clear();
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            choice = 0;
        }
    }
    
    // Clear input buffer
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    
    return choice;
}

void Game::processCommand(int choice) {
    int index = choice - 1;
    int optionCount = 0;
    
    // Process movement options
    const auto& exits = currentRoom->getExits();
    if (index < static_cast<int>(exits.size())) {
        auto it = exits.begin();
        std::advance(it, index);
        movePlayer(it->first);
        return;
    }
    optionCount += exits.size();
    
    // Process item pickup options
    const auto& items = currentRoom->getItems();
    if (index < optionCount + static_cast<int>(items.size())) {
        takeItem(index - optionCount);
        return;
    }
    optionCount += items.size();
    
    // Process attack enemy options
    const auto& enemies = currentRoom->getEnemies();
    if (index < optionCount + static_cast<int>(enemies.size())) {
        attackEnemy(index - optionCount);
        return;
    }
    optionCount += enemies.size();
    
    // Process view inventory option
    if (index == optionCount) {
        showInventory();
        return;
    }
    optionCount++;
    
    // Process view status option
    if (index == optionCount) {
        showStats();
        return;
    }
}

void Game::movePlayer(const std::string& direction) {
    Room* nextRoom = currentRoom->getExit(direction);
    if (nextRoom) {
        currentRoom = nextRoom;
        std::cout << "You moved to the room on the " << direction << " side." << std::endl;
        
        // Apply new room effect
        currentRoom->applyRoomEffect(*player);
        
        // If it's a treasure room, try to generate a random item
        if (currentRoom->getType() == RoomType::TREASURE) {
            static std::random_device rd;
            static std::mt19937 gen(rd());
            std::uniform_real_distribution<> dis(0.0, 1.0);
            
            if (dis(gen) < 0.2) { // 20% chance to generate a random item upon entry
                std::cout << "You found a hidden item!" << std::endl;
                currentRoom->addRandomItem();
            }
        }
    } else {
        std::cout << "There is no exit in that direction!" << std::endl;
    }
}

void Game::takeItem(size_t index) {
    Item* item = currentRoom->takeItem(index);
    if (item) {
        // Use += operator to add item
        *player += item;
    } else {
        std::cout << "Failed to pick up item!" << std::endl;
    }
}

void Game::attackEnemy(size_t index) {
    const std::vector<Enemy*>& enemies = currentRoom->getEnemies();
    if (index < enemies.size()) {
        Enemy* enemy = enemies[index];
        
        // Use + operator to attack enemy
        *player + *enemy;
        
        // If enemy is dead, remove from room
        if (!enemy->isAlive()) {
            currentRoom->removeEnemy(index);
        } 
        // If enemy is alive, counterattack player
        else {
            // Use + operator to represent enemy attacking player
            *enemy + *player;
        }
    } else {
        std::cout << "Failed to attack enemy!" << std::endl;
    }
}

void Game::showInventory() const {
    player->displayInventory();
}

void Game::showStats() const {
    std::cout << "===== CHARACTER STATUS =====" << std::endl;
    player->display();
    std::cout << "===================" << std::endl;
}

void Game::checkGameOver() {
    // Check if player is dead
    if (!player->isAlive()) {
        gameOver = true;
        playerWon = false;
        return;
    }
    
    // Check if victory conditions are met (e.g., reached treasure room and defeated all enemies)
    if (currentRoom == rooms[3] && currentRoom->getEnemies().empty()) {
        gameOver = true;
        playerWon = true;
        return;
    }
}

bool Game::isGameOver() const {
    return gameOver;
}

bool Game::hasPlayerWon() const {
    return playerWon;
} 